/*
 *  WeightEntryWindow.java 
 *    
 *    Copyright 2012 YAWP Project Team
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.googlecode.yawp.view;
import java.awt.BorderLayout;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;

import java.awt.Color;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;


import javax.swing.SwingConstants;
import javax.swing.JTextField;

import com.googlecode.yawp.controller.Controller;
import com.googlecode.yawp.model.Model;
import com.googlecode.yawp.model.Preferences;

/**
 * Simple Weight Entry Window for entering one Weight for a given Date
 * 
 * @author ccato
 * @author Dan Spuches
 *
 */
@SuppressWarnings("serial")
public class WeightEntryWindow extends JFrame {

	private JPanel contentPane;
	private JButton btnAddWeightAndClose;
	private JButton btnAddWeight;
	private JButton btnClose;
	private JButton btnDelete;
	private JFormattedTextField ftfDate;
	private JTextField ftfWeight;
	
	private String[] unitName = { "LBS", "KGS" };

	private Model model;
	
	private String[] days = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
	
	private JLabel[] dayLabels;
	private JButton[] dayButtons;
	private JPanel pnlCalendar;
	private JPanel pnlDateSelector;
	private JComboBox jcbMonths;
	private JComboBox jcbYears;
	private String[] months = {"January", 
			   "February",
			   "March", 
			   "April", 
			   "May", 
			   "June", 
			   "July", 
			   "August", 
			   "September", 
			   "October", 
			   "November", 
			   "December"
	};

	private Integer[] years;

	/**
	 * Constructor - Create the frame.
	 */
	public WeightEntryWindow(Model model) {
		this.model = model;
		setTitle("YAWP - Enter Weight (day)");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 550, 300);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(new BorderLayout(0, 0));
		setContentPane(contentPane);
		
		
		
		// holds button area
		JPanel panel = new JPanel();
		contentPane.add(panel, BorderLayout.SOUTH);
		
		// create and add buttons
		btnAddWeight = new JButton("Add Weight");
		panel.add(btnAddWeight);
		btnAddWeightAndClose = new JButton("Add Weight & Close");
		panel.add(btnAddWeightAndClose);
		
		btnDelete = new JButton("Delete");
		panel.add(btnDelete);
		btnClose = new JButton("Close");
		panel.add(btnClose);
		
		// holds date and weight entry fields
		JPanel panel_1 = new JPanel();
		contentPane.add(panel_1, BorderLayout.NORTH);
		panel_1.setLayout(new GridLayout(0, 2, 0, 0));
		
		// create date fields
		JLabel lblNewLabel = new JLabel("Date: ");
		lblNewLabel.setHorizontalAlignment(SwingConstants.TRAILING);
		panel_1.add(lblNewLabel);
		ftfDate = new JFormattedTextField(DateFormat.getDateInstance(DateFormat.SHORT));
		ftfDate.setValue(new Date());
		ftfDate.setColumns(10);
		ftfDate.setEditable(false);
//		ftfDate.setText(new Date().toString());
		panel_1.add(ftfDate);
		
		// create weight fields
		JLabel lblNewLabel_1 = new JLabel("Weight: ");
		lblNewLabel_1.setHorizontalAlignment(SwingConstants.TRAILING);
		panel_1.add(lblNewLabel_1);
		ftfWeight = new JTextField();
		ftfWeight.setColumns(10);
		Calendar today = Calendar.getInstance();
		// populate the weight field if today has a weight entry
		if (model.hasExistingWeightEntry(today)) {
			DecimalFormat dFormat = new DecimalFormat("#.#");
	        double weight = Double.valueOf(dFormat.format(model.getDayWeight(today)));
			ftfWeight.setText(Double.toString(weight));
		}
		panel_1.add(ftfWeight);
		
		// Print out units being used
		JLabel lblNewLabel_2 = new JLabel("Units: ");
		lblNewLabel_2.setHorizontalAlignment(SwingConstants.TRAILING);
		panel_1.add(lblNewLabel_2);
		JLabel lblNewLabel_3 = new JLabel(Preferences.getWeightUnitName(model.getUserUnits()));
		panel_1.add(lblNewLabel_3);
		
		// Create calendar panel
		pnlCalendar = new JPanel();
		pnlCalendar.setLayout(new GridLayout(6, 7));
		
		// first row consists of day labels
		dayLabels = new JLabel[days.length];
		for (int i = 0; i < days.length; i++) {
			dayLabels[i] = new JLabel(days[i]);
			pnlCalendar.add(dayLabels[i]);
		}
		
		// create the remaining buttons in the grid
		dayButtons = new JButton[35];
		for (int i = 0; i < 35; i++) {
			dayButtons[i] = new JButton();
			pnlCalendar.add(dayButtons[i]);
		}
		
		// go update the calendar with today's date
		updateCalendar(today);
		
		contentPane.add(pnlCalendar, BorderLayout.CENTER);
		
		// create date selector combo boxes to allow user to change year or month
		pnlDateSelector = new JPanel();
		pnlDateSelector.setLayout(new GridBagLayout());
		
		jcbMonths = new JComboBox(months);
		jcbMonths.setSelectedIndex(today.get(Calendar.MONTH));
		
		pnlDateSelector.add(jcbMonths);
		Integer year = today.get(Calendar.YEAR);
		
		// display the previous 100 years
		years = new Integer[100];
		for (int i = 0; i < 100; i++) {
			years[i] = year;
			year--;
		}
		jcbYears = new JComboBox(years);
		
		pnlDateSelector.add(jcbYears);
		
		
		jcbMonths.addActionListener(new ComboBoxActionListener());
		jcbYears.addActionListener(new ComboBoxActionListener());
		contentPane.add(pnlDateSelector, BorderLayout.WEST);
	}
	
	/**
	 * Registers a Listener for the Add Weight & Close Button
	 * 
	 *  @param listener The action listener to assign to the button
	 */
	public void addNewWeightAndCloseListener(ActionListener listener) {
		btnAddWeightAndClose.addActionListener(listener);
	}
	
	/**
	 * Registers a Listener for the Add Weight Button
	 * 
	 *  @param listener The action listener to assign to the button
	 */
	public void addNewWeightListener(ActionListener listener) {
		btnAddWeight.addActionListener(listener);
	}
	
	
	/**
	 * Register a listener for the Delete button
	 * 
	 * @param listener The action listener to assign to the button
	 */
	public void addDeleteWeightListener(ActionListener listener) {
		btnDelete.addActionListener(listener);
	}
	
	/**
	 * Register a listener for the Close button
	 * 
	 * @param listener The action listener to assign to the button
	 */
	public void addCloseListener(ActionListener listener) {
		btnClose.addActionListener(listener);
	}
	
	/**
	 * Get the weight in the weight text box
	 * @return Weight
	 */
	public String getWeight() {
		return ftfWeight.getText();
	}
	
	/**
	 * Get the Date entered in the date text box
	 * @return Date
	 */
	public Date getDate() {
		return (Date)ftfDate.getValue();
	}

	
	/**
	 * Updates the calendar panel to display the month specified by the parameter
	 * 
	 * @param date The month to display in the calendar
	 */
	public void updateCalendar(Calendar date) {
		// get current date information
		int year = date.get(Calendar.YEAR);
		int month = date.get(Calendar.MONTH);
		int dayOfMonth = date.get(Calendar.DAY_OF_MONTH);
		int dayOfWeek = date.get(Calendar.DAY_OF_WEEK);
		int daysInMonth = date.getActualMaximum(Calendar.DAY_OF_MONTH);
		int offset;
		
		// determine the first day of the month
		Calendar firstDay = date;
		firstDay.set(Calendar.DAY_OF_MONTH, 1);
		int firstDayOfMonth = firstDay.get(Calendar.DAY_OF_WEEK);
				
		// determine offset of week of the first day of the month
		switch(firstDayOfMonth) {
		case Calendar.SUNDAY:
			offset = 0;
			break;
		case Calendar.MONDAY:
			offset = 1;
			break;
		case Calendar.TUESDAY:
			offset = 2;
			break;
		case Calendar.WEDNESDAY:
			offset = 3;
			break;
		case Calendar.THURSDAY:
			offset = 4;
			break;
		case Calendar.FRIDAY:
			offset = 5;
			break;
		case Calendar.SATURDAY:
			offset = 6;
			break;
		default:
			offset = -1;
		}
		
		// loop control variables
		int i = 0;
		int monthDays = 1;
		Calendar today = Calendar.getInstance();
		
		// loop through the entire grid of days and assign text to buttons as necessary
		while (i < dayButtons.length) {
			
			// these are the days leading up to the first day in the month.
			if (i < offset || monthDays > daysInMonth) {
				dayButtons[i].setText(" ");
				dayButtons[i].setEnabled(false);
				ActionListener[] listeners = dayButtons[i].getActionListeners();
				// remove any action listeners
				for (ActionListener l : listeners) {
					dayButtons[i].removeActionListener(l);
				}
			// these are days of the month
			} else if (monthDays <= daysInMonth){
				dayButtons[i].setText(Integer.toString(monthDays));
				
				// if the current day has a weight entry, change the color to green
				if (model.hasExistingWeightEntry(new GregorianCalendar(year, month, monthDays))) {
					dayButtons[i].setForeground(Color.GREEN);
				} else {
					dayButtons[i].setForeground(Color.BLACK);
				}
				// the specified date is in the future, disable the button
				int comparison = Controller.compareDates(today, new GregorianCalendar(year, month, monthDays));
				if (comparison < 0) {
					dayButtons[i].setEnabled(false);
				} else {
					dayButtons[i].setEnabled(true);
				}
				
				monthDays++;
				dayButtons[i].addActionListener(new DateButtonActionListener());
			}			
			
			i++;
		}
		
		// finally, update the weight field...
		Date selectedDate = getDate();
		Calendar cal = Calendar.getInstance();
		cal.setTime(selectedDate);
		if (model.hasExistingWeightEntry(cal)) {
			DecimalFormat dFormat = new DecimalFormat("#.#");
	        double weight = Double.valueOf(dFormat.format(model.getDayWeight(cal)));
			ftfWeight.setText(Double.toString(weight));
		} else {
			ftfWeight.setText("");
		}
	}
	
	/**
	 * Private inner class to listen for changes to the combo boxes in the window.
	 * This Action listener causes the calendar to get updated with the current 
	 * selections in the combo boxes.
	 * 
	 * @author Dan Spuches
	 *
	 */
	private class ComboBoxActionListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			int month = jcbMonths.getSelectedIndex();
			//System.out.println("Month" + month);
			Integer year = (Integer)jcbYears.getSelectedItem();
			//System.out.println("Year" + year);
			Calendar temp = new GregorianCalendar(year.intValue(), month, 1);
			updateCalendar(temp);
		}
		
	}
	
	/**
	 * Private inner class to listen for changes to the buttons in the calendar area.
	 * When a button is clicked, the date field gets updated with the clicked on date.
	 * 
	 * @author Dan Spuches
	 *
	 */
	private class DateButtonActionListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			
			JButton clicked = (JButton)e.getSource();
			int day = Integer.parseInt(clicked.getText());
			int month = jcbMonths.getSelectedIndex();
			int year = years[jcbYears.getSelectedIndex()];
			
			System.out.println("Year: " + year);
			System.out.println("Month: " + month);
			System.out.println("Day: " + day);
			
			Calendar date = new GregorianCalendar(year, month, day);
			ftfDate.setValue(date.getTime());
			
			if (model.hasExistingWeightEntry(date)) {
				DecimalFormat dFormat = new DecimalFormat("#.#");
		        double weight = Double.valueOf(dFormat.format(model.getDayWeight(date)));
				ftfWeight.setText(Double.toString(weight));
				btnDelete.setEnabled(true);
			} else {
				ftfWeight.setText("");
				btnDelete.setEnabled(false);
			}
			
			ftfWeight.requestFocusInWindow();
			ftfWeight.selectAll();
		}
		
	}
	
}
